/* 
* Contributors: Youngjae Kim (youkim@cse.psu.edu)
*               Aayush Gupta (axg354@cse.psu.edu_
*   
* In case if you have any doubts or questions, kindly write to: youkim@cse.psu.edu 
*
* This source plays a role as bridiging disksim and flash simulator. 
* 
* Request processing flow: 
*
*  1. Request is sent to the simple flash device module. 
*  2. This interface determines FTL type. Then, it sends the request
*     to the lower layer according to the FTL type. 
*  3. It returns total time taken for processing the request in the flash. 
*
*/

#include "ssd_interface.h"
#include "disksim_global.h"
#include "ssd_global.h"
#include "dftl.h"

extern int merge_switch_num;
extern int merge_partial_num;
extern int merge_full_num;
int old_merge_switch_num = 0;
int old_merge_partial_num = 0;
int old_merge_full_num= 0;
int old_flash_gc_read_num = 0;
int old_flash_erase_num = 0;
int req_count_num = 1;
int cache_hit, rqst_cnt;
int flag1 = 1;
int count = 0;

/***********************************************************************
 Variables for statistics    
***********************************************************************/
unsigned int cnt_read = 0;
unsigned int cnt_write = 0;
unsigned int cnt_delete = 0;
unsigned int cnt_evict_from_flash = 0;
unsigned int cnt_evict_into_disk = 0;
unsigned int cnt_fetch_miss_from_disk = 0;
unsigned int cnt_fetch_miss_into_flash = 0;

double sum_of_queue_time = 0.0;
double sum_of_service_time = 0.0;
double sum_of_response_time = 0.0;
unsigned int total_num_of_req = 0;


/***********************************************************************
 *    author: zj    封装 callFsim的代码函数
 ***********************************************************************/
void SecnoToPageno(int secno,int scount,unsigned int *blkno,int *bcount);
double calculate_delay_flash();
void initFlash();
void endFlash();
void printWearout();
double callFsim(unsigned int secno, int scount, int operation);
void send_flash_request(int start_blk_no, int block_cnt, int operation, int mapdir_flag);

FILE *fp_flash_stat;
FILE *fp_gc;
FILE *fp_gc_timeseries;
double gc_di =0 ,gc_ti=0;


double calculate_delay_flash()
{
    double delay;
    double read_delay, write_delay;
    double erase_delay;
    double gc_read_delay, gc_write_delay;
    double oob_write_delay, oob_read_delay;

    oob_read_delay  = (double)OOB_READ_DELAY  * flash_oob_read_num;
    oob_write_delay = (double)OOB_WRITE_DELAY * flash_oob_write_num;

    read_delay     = (double)READ_DELAY  * flash_read_num; 
    write_delay    = (double)WRITE_DELAY * flash_write_num; 
    erase_delay    = (double)ERASE_DELAY * flash_erase_num; 

    gc_read_delay  = (double)GC_READ_DELAY  * flash_gc_read_num; 
    gc_write_delay = (double)GC_WRITE_DELAY * flash_gc_write_num; 


    delay = read_delay + write_delay + erase_delay + gc_read_delay + gc_write_delay + 
        oob_read_delay + oob_write_delay;

    if( flash_gc_read_num > 0 || flash_gc_write_num > 0 || flash_erase_num > 0 ) {
        gc_ti += delay;
    }
    else {
        gc_di += delay;
    }

    if(warm_done == 1){
        fprintf(fp_gc_timeseries, "%d\t%d\t%d\t%d\t%d\t%d\n", 
        req_count_num, merge_switch_num - old_merge_switch_num, 
        merge_partial_num - old_merge_partial_num, 
        merge_full_num - old_merge_full_num, 
        flash_gc_read_num,
        flash_erase_num);

        old_merge_switch_num = merge_switch_num;
        old_merge_partial_num = merge_partial_num;
        old_merge_full_num = merge_full_num;
        req_count_num++;
    }

    reset_flash_stat();

    return delay;
}


/***********************************************************************
 Initialize Flash Drive 
***********************************************************************/

void initFlash()
{
    blk_t total_blk_num;
    blk_t total_util_blk_num;
    blk_t total_extr_blk_num;

    // total number of sectors    
    total_util_sect_num  = flash_numblocks;
    total_extra_sect_num = flash_extrblocks;
    total_sect_num = total_util_sect_num + total_extra_sect_num; 

    // total number of blocks 
    total_blk_num = total_sect_num / SECT_NUM_PER_BLK;     // total block number
    total_util_blk_num = total_util_sect_num / SECT_NUM_PER_BLK;    // total unique block number

    global_total_blk_num = total_util_blk_num;

    total_extr_blk_num = total_blk_num - total_util_blk_num;        // total extra block number

    ASSERT(total_extr_blk_num != 0);

    if (nand_init(total_blk_num, 3) < 0) {
        EXIT(-4); 
    }

    switch(ftl_type){

        // pagemap
        case 1: ftl_op = pm_setup(); break;
        // blockmap
        //case 2: ftl_op = bm_setup(); break;
        // o-pagemap 
        case 3: ftl_op = opm_setup(); break;
        // fast
        case 4: ftl_op = lm_setup(); break;

        default: break;
    }

    ftl_op->init(total_util_blk_num, total_extr_blk_num);

    nand_stat_reset();
}

void printWearout()
{
    int i;
    FILE *fp = fopen("wearout", "w");
    
    for(i = 0; i<nand_blk_num; i++)
    {
        fprintf(fp, "%d %d\n", i, nand_blk[i].state.ec); 
    }

    fclose(fp);
}


void endFlash()
{
    nand_stat_print(outputfile);
    //ftl_op->end;
    nand_end();
}  

int youkim_flag1=0;

double callFsim(unsigned int secno, int scount, int operation)
{
    double delay; 
    int bcount;
    unsigned int blkno; // pageno for page based FTL
    int cnt,z; int min_ghost;


    if(ftl_type == 1){ }

    if(ftl_type == 3) {
        page_num_for_2nd_map_table = (opagemap_num / MAP_ENTRIES_PER_PAGE);
        
        // if(youkim_flag1 == 0 ) {
        //     youkim_flag1 = 1;
        //     init_arr();
        // }

        if((opagemap_num % MAP_ENTRIES_PER_PAGE) != 0){
            page_num_for_2nd_map_table++;
        }
    }
    //扇区和页对齐,转化 
    SecnoToPageno(secno,scount,&blkno,&bcount);

    cnt = bcount;

    switch(operation)
    {
        //write/read
        case 0:
        case 1:

        while(cnt > 0)
        {
            cnt--;

            switch(ftl_type)
            {
                case 1:
                    // page based FTL
                    send_flash_request(blkno*4, 4, operation, 1); 
                    blkno++;
                    break;
                case 2:
                    // blck based FTL
                    send_flash_request(blkno*4, 4, operation, 1); 
                    blkno++;
                    break;
                case 4: 
                    // FAST scheme 
                    if(operation == 0){ write_count++; }
                    else {read_count++;}
                    send_flash_request(blkno*4, 4, operation, 1); //cache_min is a page for page baseed FTL
                    blkno++;
                    break;
                case 3:
                    // SDFTL scheme
                    // SDFTL_Scheme(&blkno,&cnt,operation);
                    // DFTL scheme
                    //DFTL_Scheme(&blkno,&cnt,operation);
                    // CPFTL scheme
                    // CPFTL_Scheme(&blkno,&cnt,operation);
                    // ADFTL scheme
                    // ADFTL_Scheme(&blkno,&cnt,operation);
                    // WRFTL scheme
                    WRFTL_Scheme(&blkno,&cnt,operation);
                    // PPFTLscheme   用于对比的，纯页级映射，切完全加载如RAM的FTL
                    //PPFTL_Scheme(&blkno,&cnt,operation);
                    break;
            }
        }
        break;
    }

    delay = calculate_delay_flash();
    return delay;
}

/************************CallFsim预处理函数**************************/
void SecnoToPageno(int secno,int scount,unsigned int *blkno,int *bcount)
{
    switch(ftl_type){
        case 1:
            // page based FTL 
            *blkno = secno / 4;
            *bcount = (secno + scount -1)/4 - (secno)/4 + 1;
            break;
        case 2:
            // block based FTL 
            *blkno = secno/4;
            *bcount = (secno + scount -1)/4 - (secno)/4 + 1;      
            break;
        case 3:
            // o-pagemap scheme
            *blkno = secno / 4;
            *blkno += page_num_for_2nd_map_table;
            *bcount = (secno + scount -1)/4 - (secno)/4 + 1;
            break;
        case 4:
            // FAST scheme
            *blkno = secno/4;
            *bcount = (secno + scount -1)/4 - (secno)/4 + 1;
            break;		
    }
}

/*************************************************************
*              author:ymb        PPFTL
* *********************************************************/

void PPFTL_Scheme(int *pageno,int *req_size,int operation)
{
    int blkno=(*pageno),cnt=(*req_size);

    rqst_cnt++;
    if(operation==0){
        write_count++;
    }
    else
        read_count++;
    send_flash_request(blkno*4, 4, operation, 1);
    blkno++;
    //syn-value
    (*pageno)=blkno,(*req_size)=cnt;
}

